/*
 * Author: ahxgw
 * Created Time:  Thu 14 Apr 2011 11:15:47 PM PDT
 * Description: 
 */
#include "events.h"
#include "utils.h"

void sig_alrm(int signo)
{
    puts("alarm");
    for (map<string, userTable>::iterator it = onlineList.begin(); it != onlineList.end(); it++)
    {
        if (it->second.HB == 0)
            onlineList.erase(it);
        else
            it->second.HB = 0;
    }
    alarm(30);
}

void do_login_ack(Node *p)
{
	userTable A;
	SAI peeraddr;
    socklen_t len;
    char *pIP;

	A.fd = p->fd;
	sscanf(p->content, "%s%s%s", A.UID, A.nickname, A.uIP);
	len = sizeof(peeraddr);
	getpeername(p->fd, (SA *) &peeraddr, &len);
	pIP = inet_ntoa(peeraddr.sin_addr);
	strcpy(A.pIP, pIP);
	A.HB = 1;
	A.iswai = !(strcmp(A.pIP, A.uIP));
    onlineList[A.UID] = A;
}

void do_friend_list_ack(Node *p)
{
    char buf[BUF_SIZE], *res;
    char UID[LEN];
    
    strcpy(buf, "");
    res = strtok(p->content, "\n");
    res = strtok(NULL, "\n");
    if (res == NULL)
        return;
    do {
        sscanf(res, "%s", UID);
        printf("UID = %s\n", UID);
        strcat(buf, res);
        if (onlineList.find(UID) != onlineList.end())
            strcat(buf, " online\n");
        else
            strcat(buf, " offline\n");
        res = strtok(NULL, "\n");
        puts(buf);
    } while(res != NULL);
    strcpy(p->content, buf);
    p->msgh.length = strlen(buf) + 1;
}

void do_logout(Node *p)
{
    onlineList.erase(p->content);
}

void do_heart_beat(Node *p)
{
	puts("heart beat!");
    onlineList[p->content].HB++;
}

void do_online_list(Node *p)
{
	char buf[LEN * 3];
	struct epoll_event ev;
	
    puts("online list");
	p->msgh.cmd = ONLINE_LIST_ACK;
    strcpy(p->content, "");
	for (map<string, userTable>::iterator it = onlineList.begin(); it != onlineList.end(); it++)
	{
		sprintf(buf, "%s %s\n", it->second.UID, it->second.nickname);
		strcat(p->content, buf);
	}
	p->msgh.length = strlen(p->content) + 1;
	ev.events = EPOLLOUT;
	ev.data.ptr = p;
	epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &ev);
}

void do_p2p_request(Node *p)
{
	int i;
	char AUID[LEN], BUID[LEN];
	userTable A, B, C;
	struct epoll_event ev;
	Node *up;

	puts("p2p requst");
	sscanf(p->content, "%s %s", AUID, BUID);
    A = onlineList[AUID];
    B = onlineList[BUID];

	p->msgh.cmd = P2P_RESPONSE;

	if ( (A.iswai && B.iswai) || (A.iswai == 0 && B.iswai) )
	{
		sprintf(p->content, "%s %s", B.UID, B.uIP);
		p->msgh.error = 0;
		p->msgh.length = strlen(p->content) + 1;
		ev.events = EPOLLOUT;
		ev.data.ptr = p;
		epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &ev);
	}
	else if (A.iswai && B.iswai == 0)
	{
		p->msgh.error = 1;
		strcpy(p->content, "wait");
		p->msgh.length = strlen(p->content) + 1;
		ev.events = EPOLLOUT;
		ev.data.ptr = p;
		epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &ev);
		
		up = new Node(B.fd);
		up->msgh.cmd = P2P_RESPONSE;
		up->msgh.error = 0;
		sprintf(up->content, "%s %s", A.UID, A.uIP);
		up->msgh.length = strlen(up->content) + 1;
		ev.events = EPOLLOUT;
		ev.data.ptr = up;
		epoll_ctl(epfd, EPOLL_CTL_MOD, up->fd, &ev);
	}
	else if (A.iswai == 0 && B.iswai == 0)
	{
        for (map<string, userTable>::iterator it = onlineList.begin(); it != onlineList.end(); it++)
            if (it->second.iswai)
            {
                C = it->second;
                break;
            }
        
		p->msgh.error = 2;
		sprintf(p->content, "%s %s %s", C.UID, C.uIP, B.UID);
		p->msgh.length = strlen(p->content) + 1;
		ev.events = EPOLLOUT;
		ev.data.ptr = p;
		epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &ev);

		up = new Node(B.fd);
		up->msgh.cmd = P2P_RESPONSE;
		up->msgh.error = 2;
		sprintf(up->content, "%s %s %s", C.UID, C.uIP, A.UID);
		up->msgh.length = strlen(up->content) + 1;
		ev.events = EPOLLOUT;
		ev.data.ptr = up;
		epoll_ctl(epfd, EPOLL_CTL_MOD, up->fd, &ev);
	}
}

void do_in(Node *p)
{
    int n, cmd;
    struct epoll_event ev;
    pipeNode *down;
	Node *up;

	if (p->fd == pfd2[0])
	{
		n = read(p->fd, &up, 4);
	}
    else if (p->state == 0)
	{
		n = read(p->fd, &p->msgh, sizeof(msgHead));
	}
	else
    {
        n = read(p->fd, p->content, p->msgh.length);
    }
    if (n == -1)
    {
        if (errno != EAGAIN)
        {
            epoll_ctl(epfd, EPOLL_CTL_DEL, p->fd, &ev);
            close(p->fd);
            err_sys("read error");
        }
        return;
    }
    else if (n == 0)
    {
        epoll_ctl(epfd, EPOLL_CTL_DEL, p->fd, &ev);
        close(p->fd);
        return;
    }

	if (p->fd == pfd2[0])
	{
        printf("read from pipe content = %s\n", up->content);
		if (up->msgh.cmd == LOGIN_ACK && up->msgh.error == 0)
			do_login_ack(up);
        if (up->msgh.cmd == FRIEND_LIST_ACK)
            do_friend_list_ack(up);
		ev.events = EPOLLOUT;
		ev.data.ptr = up;
		epoll_ctl(epfd, EPOLL_CTL_MOD, up->fd, &ev);
	}
	else if (p->state == 0)
	{
        printf("cmd = %lu\n", p->msgh.cmd);
		p->state = 1;
	}
	else
	{
        cmd = p->msgh.cmd;
        if (cmd == REGISTER || cmd == LOGIN || cmd == FRIEND_LIST || cmd == FRIEND_CTL)
        {
		    down = new pipeNode(p->fd, p->msgh, p->content);
		    nodeBuf[qe++] = down;
		    qe %= BUF_SIZE;
        }
        else if (cmd == LOGOUT)
            do_logout(p);
        else if (cmd == HEART_BEAT)
            do_heart_beat(p);
        else if (cmd == ONLINE_LIST)
            do_online_list(p);
        else if (cmd == P2P_REQUST)
            do_p2p_request(p);
        p->state = 0;
	}
}

void do_out(Node *p)
{
	int n;
	struct epoll_event ev;

	if (p->fd == pfd1[1])
    {
        if (qh != qe)
        {
		    n = write(p->fd, &nodeBuf[qh], 4);
        }
        else
            return;
	}
	else if (p->state == 0)
	{
		n = write(p->fd, &p->msgh, sizeof(msgHead));
	}
	else
	{
		n = write(p->fd, p->content, p->msgh.length);
	}
	if (n == -1)
    {
        if (errno != EAGAIN)
        {
            epoll_ctl(epfd, EPOLL_CTL_DEL, p->fd, &ev);
            close(p->fd);
            err_sys("read error");
        }
        return;
    }
    else if (n == 0)
    {
        epoll_ctl(epfd, EPOLL_CTL_DEL, p->fd, &ev);
        close(p->fd);
        return;
    }
	
	if (p->fd == pfd1[1])
	{
        if (qh != qe)
        {
            printf("write to pipe\n");
		    qh++;
		    qh %= BUF_SIZE;
        }
	}
	else if (p->state == 0)
	{
		p->state = 1;
	}
	else
	{
        if (p->msgh.cmd == REGISTER_ACK)
        {
		    epoll_ctl(epfd, EPOLL_CTL_DEL, p->fd, &ev);
            close(p->fd);
		    delete p;
        }
        else
        {
            printf("epoll mod to in\n");
            ev.events = EPOLLIN;
            ev.data.ptr = p;
            p->state = 0;
            epoll_ctl(epfd, EPOLL_CTL_MOD, p->fd, &ev);
        }
	}
}
